#ifndef _EDGY_H
#define	_EDGY_H

#include <utility> // pair<>
#include <vector>
#include <string>
#include "gfblib.h"

/*** SHARED X CONNECTION ***/
//
#include <X11/Xlib.h>
#include <X11/Xutil.h> // Class hint stuff
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>
#include <X11/cursorfont.h>
#include <X11/Xcursor/Xcursor.h> // XcursorLibraryLoadCursor

void configEdgy(); // sets up the font etc

/*** Place ***/

class Place {

public:

  // Don't call this, use the constants in edgy.cc.
  // The name is used by the gui
  Place(int h_sign, int v_sign, string name);

  bool operator==(const Place& r) const;

  bool contains(const pair<int, int>& coords) const; // it worried that simply pair<int, int> might mutate

  bool contains(const Place& r) const;

  pair<int, int> position() const;
  pair<int, int> dimensions() const;
  
  string name() const;

private:
  const int h_sign, v_sign;
  const string sym_name;

};

/* Forward declare these, so that we could refer to them in configdata.cc */
extern const Place TOP;
extern const Place BOTTOM;
extern const Place LEFT;
extern const Place RIGHT;
extern const Place TOP_LEFT;
extern const Place TOP_RIGHT;
extern const Place BOTTOM_LEFT;
extern const Place BOTTOM_RIGHT;

/*** Trigger ***/

class Trigger {
public:
  Trigger(unsigned int button, Place p, char* focus_class);
  bool applies(pair<int, int> mouse_position, unsigned int button) const;
  Place place() const;
  unsigned int button() const;
  const char* window_class() const;
private:
  const unsigned int b;
  const Place p;
  const char* const focus_class;
};

/*** ACTIONS ***/

class Action {
public:
  // the constructor and destructor are here to quell a compiler warning about virtual destructors.
  Action() {}
  virtual ~Action() {}
  virtual void act(pair<int, int> mouse_position, unsigned int button) = 0;
};

// Mainly for spacer in Gesture_Menu.
//
class No_Action : public Action {
public:
  // [Does virtual have to be restated?]
  virtual void act(pair<int, int> mouse_position, unsigned int button);
};

// To execute: /bin/sh -c command &
//
class System_Action : public Action {
public:
  System_Action(char* command);
  virtual void act(pair<int, int> mouse_position, unsigned int button);
  
  const char* command() const;
protected:
  const char* const cmd; // [doesn't prevent delete]
  const char* const clean_cmd; // no " &" at the end
};

// For execution by sawfish-client.
//   
class Sawfish_Action : public System_Action {
public:
  // form must use quote (or `) instead of ', all other characters okay.
  Sawfish_Action(char* form);
};

// Press (and release) of a key, with 0/1/2 modifiers,
//  optionally moving mouse pointer to place on focused window.
//
// [Had moving version as subclass, could also have given moving version a Key_Action
//   But ultimately, for our purposes, a sequence action might be good.]
//
class Key_Action : public Action {
public:
  // [There's no real distinction between keys and modifiers here]
  Key_Action(KeySym k1, KeySym k2, KeySym k3);
  Key_Action(Place p, KeySym k1, KeySym k2, KeySym k3);
  virtual void act(pair<int, int> mouse_position, unsigned int button);
  
  const Place* place();
  const KeySym key1();
  const KeySym key2();
  const KeySym key3();
private:
  const Place* p;
  const KeySym k1, k2, k3;
};

/*** Menu Types ***/

struct menu_item {
  const char* label;
  Action* action; // const here causes problems when we use it
};

class Menu : public Action {

public:

  // Initialize for look of all menus.
  //
  static Font font;
  static GC gc;
  static GC highlight_gc;
  static unsigned long background_color;

  static Menu* active_menu;
  virtual void act(pair<int, int> mouse_position, unsigned int button);

  // Call to activate menu.
  virtual void begin_interaction(pair<int, int> mouse_position, unsigned int button);

  // Receive grabbed events from main loop, during interaction.
  // Return: whether interaction done.
  virtual void handle(XEvent* e);

protected:

  Window window; // Subclasses: initialize in constructor.

  // Override for per-interaction initialization,
  //  except for MapRaise, and pointer grab.
  virtual void beginning_interaction(pair<int, int> mouse_position, unsigned int button);

  // Override to handle events during interaciton.
  // Pass control to end_interaction() if done interacting.
  virtual void handling(XEvent* e) = 0;

  // See handling(XEvent*).
  virtual void end_interaction();

};

// Consider option: trigger on press.
//
//   Background styled by Menu::background_color.
//   Text styled by Menu::font, Menu::gc, Menu::highlight_gc.
//   Vertical_Menu::pad pixels on each side, 2*pad between lines.
//
class Vertical_Menu : public Menu {

public:

  static int pad;

  // [convention for num_* before/after]
  Vertical_Menu(menu_item* items, unsigned int num_items, const char* name);
  Vertical_Menu(menu_item* items, unsigned int num_items);
  
  const unsigned int size();
  const menu_item* items();
  char* name();
  void setName(const char* newName);

protected:

  // Show to bottom-right of pointer, but don't go off-screen.
  virtual void beginning_interaction(pair<int, int> mouse_position, unsigned int button);

  // Draw on Expose and MotionNotify, highlighting item under pointer (if any).
  // Scrolling scrolls, other ButtonRelease selects if on item, other ButtonPress aborts.
  virtual void handling(XEvent* e);

  virtual void draw(int x, int y);
  
private:

  int num_items;
  menu_item* m_items;
  char* menu_name;

  int width, height;

  int v_increment, ascent;

  bool contains(int x, int y);
  int item_index(int y);
  void init(); // due to two constructors

};

// Abstract class for menus only receiving events.
//   Subclasses: don't need to make the window.
class Invisible_Menu : public Menu {
protected:
  Invisible_Menu();
};

// [Later: button -> (initial key action, key action, end key action)]
class Stateful_Key : public Invisible_Menu {
public:
  Stateful_Key(KeySym m,
		    unsigned int button1, KeySym k1,
		    unsigned int button2, KeySym k2);
  
  const unsigned int button1();
  const unsigned int button2();
  const KeySym modifier();
  const KeySym key1();
  const KeySym key2();

protected:
  virtual void beginning_interaction(pair<int, int> mouse_position, unsigned int button);
  virtual void handling(XEvent* e);
  virtual void end_interaction();

private:
  KeySym m, k1, k2;
  unsigned int b1, b2;

};

class Gesture_Menu : public Invisible_Menu {

public:

  static unsigned int neutral_radius;
  static unsigned int neutral_cursor_shape;

  // Divides up semi or quarter circle into num_items segment clockwise.
  //   For padding, use items with No_Action.
  //
#ifdef OLD_CONFIG
  Gesture_Menu(Place place, menu_item* items, unsigned int num_items, unsigned int* cursor_shapes);
  const unsigned int* shapes();
#else
  Gesture_Menu(Place place, menu_item* items, unsigned int num_items, vector<string>& shape_names);
  Gesture_Menu(Place place, menu_item* items, unsigned int num_items, vector<string>& shape_names,
          const char* menuName, const char* cursorSetName);
  const vector<string> shapes();
  char* menuName();
  void setMenuName(const char* newName);
  char* cursorSetName();
  void setCursorSetName(const char* newName);
#endif
  
  const unsigned int size();
  const Place place();
  const menu_item* items();

protected:

  virtual void beginning_interaction(pair<int, int> mouse_position, unsigned int button);

  virtual void handling(XEvent* e);

private:

  Place p;
  unsigned int num_items;

  menu_item* m_items;
#ifdef OLD_CONFIG
  unsigned int* cursor_shapes;
#else
  Cursor* cursor_shapes;
  vector<string> named_shapes; // stored so that the gui wouldn't have to convert from Cursor
  char* mName;
  char* csName;
  void init(); // because of two constructors
#endif

  void update_cursor(int x, int y);

  int item_index(int x, int y);

};

// Effects consist of a trigger and an action
struct effect {
  Trigger *t;
  Action* a;
};

#endif	/* _EDGY_H */

